﻿using System;
using System.Collections.Generic;
using System.Linq;
using Croxweb.Leuphana.Models;
using Croxweb.Leuphana.Mws;
using Croxweb.Leuphana.Services.StorageService;
using System.ServiceModel;
using System.ComponentModel;

namespace Croxweb.Leuphana.Services.MyStudyService
{
    public class MyStudyService : IMyStudyService
    {
        private MyStudyCredentials credentials;

        private readonly IScheduleStore storage;

        // Constructor.
        public MyStudyService(MyStudyCredentials credentials)
        {
            this.credentials = credentials;

            this.storage = new PhoneScheduleStore();
        }

        public void SetCredentials(MyStudyCredentials credentials)
        {
            this.credentials = credentials;
        }


        // Lecture functions.
        public Lecture GetLecture(int id)
        {
            var lecture = from Lecture l in storage.GetLectures()
                          where l.Id == id
                          select l;

            return lecture.First();
        }


        // Appointment functions.
        public Appointment GetAppointment(int id)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Appointment> GetAllAppointments()
        {
            return storage.GetAppointments().OrderBy(a => a.StartDate);
        }

        public NextEventList GetNextAppointments(int limit)
        {
            DateTime nextDay = DateTime.Today;

            List<Appointment> appointments = new List<Appointment>();

            while (appointments.Count < 1 && nextDay < DateTime.Today.AddDays(14))
            {
                appointments = (from a in GetAllAppointments()
                                where a.EndDate > nextDay && a.StartDate <= nextDay && (a.Recurrence == RecurrenceType.None || a.StartDate.DayOfWeek == nextDay.DayOfWeek)
                                orderby a.StartTime ascending
                                select a).ToList();

                nextDay = nextDay.AddDays(1);
            }

            return new NextEventList(nextDay, appointments);
        }

        public IEnumerable<Appointment> GetLectureAppointments(int lectureId)
        {
            var result = from Appointment a in GetAllAppointments()
                         where a.LectureId == lectureId
                         select a;

            return result;
        }


        // Message functions.
        public Message GetMessage(int id)
        {
            var messages = from Message m in storage.GetMessages()
                           where m.Id == id
                           select m;

            return messages.First();
        }

        public IEnumerable<Message> GetLatestMessages(int limit)
        {
            var messages = from Message m in storage.GetMessages()
                           orderby m.Received descending
                           select m;

            if (messages.Count() > limit)
            {
                return messages.ToList().GetRange(0, limit);
            }
            return messages;
        }

        public IEnumerable<Message> GetLectureMessages(int lectureId)
        {
            var messages = from Message m in storage.GetMessages()
                           where m.LectureId == lectureId
                           orderby m.Received descending
                           select m;

            return messages;
        }


        // User functions.
        public void Authorize(String password, Action<MyStudyResult, String> callback)
        {
            leuphanaSoapPortClient service = new leuphanaSoapPortClient();
            service.getPassHashCompleted += (sender, e) =>
                {
                    if (e.Error == null)
                    {
                        callback(MyStudyResult.Success, e.Result);
                    }
                    else if (e.Error is EndpointNotFoundException)
                    {
                        callback(MyStudyResult.UnreachableServer, String.Empty);
                    }
                    else
                    {
                        callback(MyStudyResult.UnknownError, String.Empty);
                    }
                };
            service.getPassHashAsync(password);
        }


        // Person functions.
        public void GetPerson(int id, Action<MyStudyResult, PersonDetail> callback)
        {
            leuphanaSoapPortClient service = new leuphanaSoapPortClient();
            service.getPersonCompleted += (sender, e) =>
                {
                    if (e.Error == null)
                    {
                        var persons = (from personDetail p in e.Result
                                       select new PersonDetail()
                                       {
                                           Id = p.id,
                                           FirstName = p.firstname,
                                           LastName = p.lastname,
                                           EmailAddress = p.email,
                                           ImageUrl = p.imageUrl,
                                           Phone = normalizePhoneNumber(p.phone),
                                           Mobile = p.mobile,
                                           Office = p.office,
                                           OfficeHours = p.officeHours,
                                           Website = p.website,
                                           ShortCv = p.shortCv,
                                           ResearchInterests = p.researchInterests,
                                           EducationInterests = p.educationInterests
                                       }).ToList();

                        if (persons.Count > 0)
                        {
                            callback(MyStudyResult.Success, persons.First());
                        }
                        else
                        {
                            callback(MyStudyResult.Success, null);
                        }
                    }
                    else
                    {
                        callback(MyStudyResult.UnknownError, null);
                    }
                };

            // Run Service request.
            service.getPersonAsync(credentials.Username, credentials.Password, id);
        }

        public void SearchPersons(String name, Action<MyStudyResult, IEnumerable<Person>> callback)
        {
            leuphanaSoapPortClient service = new leuphanaSoapPortClient();
            service.searchPersonsCompleted += (sender, e) =>
                {
                    if (e.Error == null)
                    {
                        var persons = (from person p in e.Result
                                       select new Person()
                                       {
                                           Id = p.id,
                                           FirstName = p.firstname,
                                           LastName = p.lastname,
                                           EmailAddress = p.email
                                       }).ToList();

                        callback(MyStudyResult.Success, persons);
                    }
                    else
                    {
                        callback(MyStudyResult.UnknownError, null);
                    }
                };

            // Run Service request.
            service.searchPersonsAsync(credentials.Username, credentials.Password, name);
        }


        // Sync functions.
        public void SynchronizeSchedule(Action<MyStudyResult> callback)
        {
            // Create webservice client and bind to completed event.
            leuphanaSoapPortClient service = new leuphanaSoapPortClient();
            service.getScheduleCompleted += (sender, e) =>
                {
                    if (e.Error == null)
                    {
                        // Create models from service dtos.
                        var lectures = createLectures(e.Result.ToList());
                        var appointments = createAppointments(e.Result.ToList());

                        // Store models locally.
                        storage.SaveLectures(lectures);
                        storage.SaveAppointments(appointments);

                        // Run callback method.
                        callback(MyStudyResult.Success);
                    }
                    else
                    {
                        if (e.Error is FaultException)
                        {
                            callback(MyStudyResult.AccessDenied);
                        }
                        else if (e.Error is EndpointNotFoundException)
                        {
                            callback(MyStudyResult.UnreachableServer);
                        }
                        else
                        {
                            callback(MyStudyResult.UnknownError);
                        }
                    }
                };

            // Run service request.
            service.getScheduleAsync(credentials.Username, credentials.Password, credentials.Semester);
        }

        public void SynchronizeMessages(Action<MyStudyResult> callback)
        {
            leuphanaSoapPortClient service = new leuphanaSoapPortClient();
            service.getMessagesCompleted += (sender, e) =>
                {
                    if (e.Error == null)
                    {
                        // Create models from service dtos.
                        var messages = createMessages(e.Result.ToList());

                        // Store models locally.
                        storage.SaveMessages(messages);

                        // Run callback method.
                        callback(MyStudyResult.Success);
                    }
                    else
                    {
                        callback(MyStudyResult.UnknownError);
                    }
                };
            service.getMessagesAsync(credentials.Username, credentials.Password, credentials.Semester, DateTime.Today.AddMonths(-12));
        }


        // Callback functions.
        private IEnumerable<Lecture> createLectures(List<lecture> lectures)
        {
            List<Lecture> result = new List<Lecture>();
            foreach (var lecture in lectures)
            {
                result.Add(new Lecture()
                {
                    Id = lecture.id,
                    Title = System.Net.HttpUtility.HtmlDecode(lecture.name),
                    Details = System.Net.HttpUtility.HtmlDecode(lecture.contents),
                    Goals = System.Net.HttpUtility.HtmlDecode(lecture.goals),
                    Sws = lecture.sws,
                    Url = lecture.url + "",
                    Persons = lecture.persons.Select((p) => new Person()
                    {
                        Id = p.id,
                        FirstName = p.firstname,
                        LastName = p.lastname,
                        EmailAddress = p.title
                    }).ToList()
                });
            }

            return result;
        }

        private IEnumerable<Appointment> createAppointments(List<lecture> lectures)
        {
            List<Appointment> result = new List<Appointment>();
            foreach (var lecture in lectures)
            {
                result.AddRange((from appointment a in lecture.appointments
                                 select new Appointment()
                                 {
                                     Id = a.id,
                                     LectureId = a.lectureid,
                                     Subject = System.Net.HttpUtility.HtmlDecode(a.subject),
                                     Lecture = System.Net.HttpUtility.HtmlDecode(lecture.name),
                                     Location = a.room,
                                     StartDate = a.startdate,
                                     EndDate = a.enddate,
                                     StartTime = a.starttime,
                                     EndTime = a.endtime,
                                     Recurrence = ((a.recurrence == 3) ? RecurrenceType.None : ((a.recurrence == 1) ? RecurrenceType.Weekly : RecurrenceType.Fortnightly))
                                 }).ToList());
            }

            return result;
        }

        private IEnumerable<Message> createMessages(List<message> messages)
        {
            List<Message> result = new List<Message>();
            foreach (var message in messages)
            {
                result.Add(new Message()
                {
                    Id = message.id,
                    LectureId = message.lectureId,
                    Received = message.received,
                    Sender = new Person()
                    {
                        Id = message.sender.id,
                        FirstName = message.sender.firstname,
                        LastName = message.sender.lastname,
                        EmailAddress = message.sender.email
                    },
                    Source = message.source,
                    Subject = System.Net.HttpUtility.HtmlDecode(message.subject),
                    Body = System.Net.HttpUtility.HtmlDecode(message.text)
                });
            }

            return result;
        }

        private String normalizePhoneNumber(String number)
        {
            if (number.StartsWith("667"))
            {
                number = "+49 4131 " + number;
            }

            if (number.Length == 4)
            {
                number = "+49 4131 667-" + number;
            }

            return number;
        }
    }
}
